package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.exceptions.DbException;
import com.tianji.common.utils.AssertUtils;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.LearningRecordFormDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.enums.SectionType;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import com.tianji.learning.service.ILearningRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.learning.utils.LearningRecordDelayTaskHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author xioayang
 * @since 2024-06-22
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final CourseClient courseClient;
    private final ILearningLessonService learningLessonService;
    private final LearningRecordDelayTaskHandler taskHandler;

    /**
     * 查询指定课程的学习记录
     *
     * @param courseId 课程id
     * @return 学习记录
     */
    @Override
    public LearningLessonDTO queryLearningRecordByCourse(Long courseId) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        // 2.查询课表
        LearningLesson learningLesson = learningLessonService.queryByUserAndCourseId(userId, courseId);
        // 3.查询学习记录
        List<LearningRecord> records = lambdaQuery().eq(LearningRecord::getLessonId, learningLesson.getId()).list();
        // 4.封装结果
        LearningLessonDTO dto = BeanUtils.copyBean(learningLesson, LearningLessonDTO.class);
        dto.setId(learningLesson.getId());
        dto.setLatestSectionId(learningLesson.getLatestSectionId());
        return dto;
    }

    /**
     * 提交学习记录
     *
     * @param recordDTO 学习记录
     */
    @Override
    public void addLearningRecord(LearningRecordFormDTO recordDTO) {
        //1、获取登录用户
        Long userId = UserContext.getUser();
        //2、是考试还是视频记录
        Boolean finished = recordDTO.getSectionType() ==
                SectionType.VIDEO ? handleVideoRecord(userId, recordDTO) : handleExamRecord(userId, recordDTO);


        //3、处理课表记录
        handleLearningLessonsChanges(recordDTO, finished);
    }

    private void handleLearningLessonsChanges(LearningRecordFormDTO recordDTO, Boolean finished) {
        // 1.查询课表
        LearningLesson lesson = learningLessonService.getById(recordDTO.getLessonId());
        AssertUtils.isNotNull(lesson, "课表数据不存在，无法更新数据");
        // 2.判断是否有新的完成小节
        boolean allLearned = false;
        if (finished) {
            // 3.如果有新完成的小节，则需要查询课程数据
            CourseFullInfoDTO courseInfo = courseClient.getCourseInfoById(lesson.getCourseId(), false, false);
            if (courseInfo == null) {
                throw new BizIllegalException("课程不存在，无法更新数据！");
            }
            // 4.比较课程是否全部学完：已学习小节 >= 课程总小节
            allLearned = courseInfo.getSectionNum() <= lesson.getLearnedSections() + 1;
        }
        // 5.更新课表
        learningLessonService.lambdaUpdate()
                .set(!finished, LearningLesson::getLatestSectionId, recordDTO.getSectionId())
                .set(!finished, LearningLesson::getLatestLearnTime, recordDTO.getCommitTime())
                .setSql(finished, "learned_sections = learned_sections + 1")
                .set(lesson.getLearnedSections() == 0, LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .set(allLearned, LearningLesson::getStatus, LessonStatus.FINISHED.getValue())
                .eq(LearningLesson::getId, lesson.getId())
                .update();
    }

    /**
     * 处理视频学习记录
     *
     * @param recordDTO
     * @return
     */
    private Boolean handleExamRecord(Long userId, LearningRecordFormDTO recordDTO) {
        // 1.转换DTO为PO
        LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
        // 2.填充数据
        record.setUserId(userId);
        record.setFinished(true);
        record.setFinishTime(recordDTO.getCommitTime());
        // 3.写入数据库
        boolean success = save(record);
        if (!success) {
            throw new DbException("新增考试记录失败！");
        }
        return true;
    }


    private Boolean handleVideoRecord(Long userId, LearningRecordFormDTO recordDTO) {
        // 1.查询旧的学习记录
        LearningRecord oldRecord = queryOldRecord(recordDTO.getLessonId(), recordDTO.getSectionId());
        // 2.判断是否存在
        if (ObjectUtil.isEmpty(oldRecord)) {
            // 3.不存在，则新增
            // 3.1.转换PO
            LearningRecord record = BeanUtils.copyBean(recordDTO, LearningRecord.class);
            // 3.2.填充数据
            record.setUserId(userId);
            // 3.3.写入数据库
            boolean success = save(record);
            if (!success) {
                throw new DbException("新增学习记录失败！");
            }
            return false;
        }
        // 4.存在，则更新
        // 4.1.判断是否是第一次完成
        boolean finished = recordDTO.getMoment() << 1 >= recordDTO.getDuration() && !oldRecord.getFinished();
        // 4.2、如果是非第一次学完，将学习进度暂存至redis，并且提交一个延时检测任务
        if(!finished){
            LearningRecord learningRecord = new LearningRecord();
            learningRecord.setLessonId(recordDTO.getLessonId());
            learningRecord.setSectionId(recordDTO.getSectionId());
            learningRecord.setId(oldRecord.getId());
            learningRecord.setMoment(recordDTO.getMoment());
            learningRecord.setFinished(oldRecord.getFinished());

            taskHandler.addLearningRecordDelayCheckTask(learningRecord);
            recordDTO.setDuration(-1);
            return false;
        }

        // 4.2.更新数据
        boolean success = lambdaUpdate()
                .set(LearningRecord::getMoment, recordDTO.getMoment())
                .set(LearningRecord::getFinished, true)
                .set(LearningRecord::getFinishTime, recordDTO.getCommitTime())
                .eq(LearningRecord::getId, oldRecord.getId())
                .update();
        if (!success) {
            throw new DbException("更新学习记录失败！");
        }
        // 4.4、清理redis缓存中的学习记录（保证Redis双写一致性）
        taskHandler.cleanLearningRecordCache(recordDTO.getLessonId(),recordDTO.getSectionId());
        return true;
    }

    private LearningRecord queryOldRecord(Long lessonId, Long sectionId) {
        // 1、先从缓存中查询学习记录
        LearningRecord learningRecord = taskHandler.readLearningRecordFromCache(lessonId, sectionId);
        // 2、如果存在，直接返回
        if (ObjectUtil.isNotEmpty(learningRecord)) {
            return learningRecord;
        }
        // 3、如果不存在，则查询数据库，并将结果放入缓存
        learningRecord = lambdaQuery().eq(LearningRecord::getLessonId, lessonId)
                .eq(LearningRecord::getSectionId, sectionId).one();
        if (ObjectUtil.isEmpty(learningRecord)) {
            return null;   // 不能抛异常，要作为调用者处理后续代码逻辑判断依据
        }
        taskHandler.writeLearningRecordToCache(learningRecord);

        // 4、返回record
        return learningRecord;
    }
}
